import { log, Quat, v3, Vec3 } from "cc";

export function linear(x0: number, x1: number, y0: number, y1: number, inputX: number): number {
    if (x0 == x1) return y0;
    return (y1 - y0) * (inputX - x0) / (x1 - x0) + y0;
}
export function linearClamp(x0: number, x1: number, y0: number, y1: number, inputX: number): number {
    let value = linear(x0, x1, y0, y1, inputX);
    if (value < Math.min(y0, y1)) value = Math.min(y0, y1);
    if (value > Math.max(y0, y1)) value = Math.max(y0, y1);
    return value;
}

const _A = v3();
const _B = v3();
const _C = v3();
const _crossBC = v3();
const _crossAB = v3();

// 线线交点
export function lineIntersection(res: Vec3, a0: Vec3, a1: Vec3, b0: Vec3, b1: Vec3) {
    Vec3.subtract(_A, a1, a0);
    _A.normalize();
    Vec3.subtract(_B, b1, b0);
    _B.normalize();

    if (Vec3.cross(_C, _A, _B).equals3f(0, 0, 0)) return false; // 叉积等于零，两直线平行

    Vec3.subtract(_C, a0, b0);
    Vec3.cross(_crossBC, _B, _C);
    Vec3.cross(_crossAB, _A, _B);
    const LenB = _crossAB.length();
    const lenAA = Vec3.dot(_crossBC, _crossAB);
    const Len = lenAA / (LenB * LenB);
    _A.multiplyScalar(Len);
    Vec3.add(res, a0, _A);
    return true;
}

/**
 * 点在面上的投影
 * @param res 投影点
 * @param A 平面外一点
 * @param S 平面上任一点
 * @param n 平面单位法向量
 */
export function pointToPlaneProjection(res: Vec3, A: Vec3, S: Vec3, n: Vec3) {
    Vec3.subtract(_A, A, S);
    let len = Vec3.dot(_A, n);
    Vec3.multiplyScalar(_A, n, -len);
    Vec3.add(res, A, _A);
    return res;
}

/**
 * 点在线上的投影
 * @param res C在线AB上的投影点
 * @param A 线上一点
 * @param B 线上另一点
 * @param C 线外一点
 */
const AB = v3();
const AC = v3();
export function pointToLineProjection(res: Vec3, A: Vec3, B: Vec3, C: Vec3) {
    Vec3.subtract(AB, B, A);
    AB.normalize();
    Vec3.subtract(AC, C, A);
    let len = Vec3.dot(AC, AB);
    AB.multiplyScalar(len);
    Vec3.add(res, A, AB);
}

/**
 * 判断点在线上
 * (三点共线？)
 */
const BC = v3();
export function pointInLine(A: Vec3, B: Vec3, C: Vec3) {
    Vec3.subtract(AC, C, A);
    Vec3.subtract(BC, C, B);
    return AC.cross(BC).equals3f(0, 0, 0);
}

/**
 * 已知三角形的两个点坐标和对边长度，求第三个点的坐标
 * n是所在平面法向量
 */
const q = new Quat();
export function pointCTriangle(res: Vec3, A: Vec3, B: Vec3, a: number, b: number, n: Vec3) {
    let c = Vec3.distance(A, B);
    let value = (a * a - b * b - c * c) / (-2 * b * c);
    // log('value', value);
    let alpha = Math.acos(value);

    // log('alpha', alpha, alpha * 180 / Math.PI);
    Vec3.subtract(AB, B, A);
    Quat.fromAxisAngle(q, n, alpha);
    Vec3.transformQuat(res, AB, q);
    res.normalize();
    res.multiplyScalar(b);
    res.add(A);
    return res;
}

/**
 * 圆平面内随机分布
 */

export function GenCircleRandomPos(r) {
    let random_theta = Math.random() * Math.PI * 2
    // 生成距离圆心的长度
    let random_r = Math.sqrt(Math.random()) * r
    let x = Math.cos(random_theta) * random_r
    let y = Math.sin(random_theta) * random_r
    return { x, y };
}

/**
 * 随机数
 */
export function GenRandom(from: number, to: number) {
    if (from > to) {
        let _temp = from;
        from = to;
        to = _temp;
    }
    return Math.random() * (to - from) + from;
}

/**
 * A需要变化的矩形长宽 => 0
 * B目标矩形长宽 => 1
 * A缩放到最大，但不超出B，求缩放率
 */

export function stretchRate(width0, height0, width1, height1) {
    let rate = 1;
    if (width0 > width1 || height0 > height1) {
        rate = Math.min(width1 / width0, height1 / height0);
    }
    return rate;
}

const _temp_p30 = new Vec3();

export class deBoorcox {

    n: number;
    pList: Vec3[];
    k: number;
    u: number;

    constructor(pList: Vec3[], k: number, even = true) {
        this.pList = pList.slice();
        if (even) {
            // 准均匀处理
            for (let i = 0; i < k - 1; i++) {
                this.pList.unshift(this.pList[0]);
                this.pList.push(this.pList[this.pList.length - 1]);
            }
        }
        this.n = this.pList.length - 1;
        this.k = k;
        this.u = k - 1;
    }

    // u取值 [k-1, n+1)
    B(u, k, i) {
        if (k == 1) {
            if (i <= u && u < i + 1) return 1;
            else return 0;
        } else {
            let coef_0 = 0, coef_1 = 0;
            if (u - i == 0 && k - 1 == 0) {
            } else {
                coef_0 = (u - i) / (k - 1);
            }
            if (i + k - u == 0 && k - 1 == 0) {
            } else {
                coef_1 = (i + k - u) / (k - 1);
            }
            return coef_0 * this.B(u, k - 1, i) + coef_1 * this.B(u, k - 1, i + 1);
        }
    }

    solve(u) {
        let P = v3(0, 0, 0);
        for (let i = 0; i <= this.n; i++) {
            // P.addSelf(this.pList[i].mul(this.B(u, this.k, i)));
            _temp_p30.set(this.pList[i]).multiplyScalar(this.B(u, this.k, i));
            P.add(_temp_p30);
        }
        return P;
    }
}